package com.wang.yupao.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wang.yupao.common.ErrorCode;
import com.wang.yupao.exception.BusinessException;
import com.wang.yupao.model.domain.Team;
import com.wang.yupao.model.domain.User;
import com.wang.yupao.model.domain.UserTeam;
import com.wang.yupao.model.dto.TeamGetQuery;
import com.wang.yupao.model.dto.TeamQuery;
import com.wang.yupao.model.enums.TeamStatusEnum;
import com.wang.yupao.model.request.TeamJoinRequest;
import com.wang.yupao.model.request.TeamQuitRequest;
import com.wang.yupao.model.request.TeamUpdateRequest;
import com.wang.yupao.model.vo.TeamUserVO;
import com.wang.yupao.model.vo.UserVO;
import com.wang.yupao.service.TeamService;
import com.wang.yupao.mapper.TeamMapper;
import com.wang.yupao.service.UserService;
import com.wang.yupao.service.UserTeamService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author 胡歌的腿毛
 * @description 针对表【team(队伍表)】的数据库操作Service实现
 * @createDate 2023-05-25 20:31:07
 */
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 创建队伍
     *
     * @param team
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    synchronized public long addTeam(Team team, User loginUser) {
        /**
         * {
         *   "description": "测试描述",
         *   "expireTime": "2023-10-10 00:00:00",  //mysql默认会加八小时 所有要把分钟带上
         *   "maxNum": 5,
         *   "name": "腿毛小队",
         *   "password": ""
         * }
         */
        log.info("teamService team: " + team);
        log.info("teamService loginUser: " + loginUser);
        //请求参数是否为空
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //是否登录，未登录不允许创建
        if (loginUser.getId() <= 0) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        final Long userId = loginUser.getId();

        //校验信息
        //1. 队伍人数 >1 && <= 20
        int maxTeamNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxTeamNum < 1 || maxTeamNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足要求");
        }

        //2.队伍标题长度 < 20
        String teamName = team.getName();
        if (StringUtils.isBlank(teamName) || teamName.length() >= 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不符合要求");
        }

        //3. 描述长度 <= 512
        String descTeam = team.getDescription();
        if (StringUtils.isNotBlank(descTeam) && descTeam.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        }

        //4.status 是否公开 不传默认为0 （公开）
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
//        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        boolean temp = false;
        for (int i = 0; i < 3; i++) {
            if (status == i) {
                temp = true;
                break;
            }
        }
        if (!temp) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }

        //5.如果status是加密状态(status = 2 )， 一定要有密码 且密码 < 32位
        String password = team.getPassword();
        if (status == 2 && (StringUtils.isBlank(password) || password.length() > 32)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不正确");
        }

        //6.超时时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");
        }

        //7. 校验用户最多创建五个队伍
        //  并发情况下，用户还是可以一瞬间创建很多个队伍 使用 synchronized
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long count = this.count(queryWrapper);
        if (count >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建五个队伍");
        }

        // 插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean res = this.save(team);
        Long teamId = team.getId();
        if (!res) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }


        //插入用户 => 队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(team.getId());
        userTeam.setJoinTime(new Date());
        res = userTeamService.save(userTeam);
        if (!res) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        return teamId;
    }

    /**
     * 根据 teamQuery 的字段 查询相匹配的所有数据
     *
     * @param teamQuery
     * @return
     */
    @Override
    public List<TeamUserVO> listTeam(TeamQuery teamQuery, boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (teamQuery != null) {
            //根据id查询
            Long id = teamQuery.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
//            List<Long> idList = teamQuery.getIdList();
//            if (CollectionUtils.isNotEmpty(idList)) {
//                queryWrapper.in("id", idList);
//            }

            //用户名和描述同时联合查询
            String searchText = teamQuery.getSearchText();
            if (StringUtils.isNotBlank(searchText)) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }

            //用户名查询
            String name = teamQuery.getName();
            if (StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }

            //用户描述查询
            String description = teamQuery.getDescription();
            if (StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }

            //根据用户最大人数查询
            Integer maxNum = teamQuery.getMaxNum();
            // 查询最大人数相等的
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }

            // 根据创建人来查询
            Long userId = teamQuery.getUserId();
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }

            // 根据状态来查询
            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {
                //如果状态没有设置 就默认设置为公开
                statusEnum = TeamStatusEnum.PUBLIC;
            }


            //如果不是管理员，并且状态是私密，就返回没有权限
            if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            queryWrapper.eq("status", status);
        }

        // 不展示已过期的队伍
        // expireTime is null or expireTime > now()
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }

        //TeamUserVO 队伍和用户的联合信息
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        // 关联查询创建人的用户信息
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            // 脱敏用户信息
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreateUser(userVO);
            }
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;

    }

    /**
     * 修改队伍信息
     *
     * @param teamUpdateRequest
     * @param loginUser
     * @return
     */
    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        /**
         * 只是修改了 teamUpdateRequest 的数据 并不会影响 TeamUser关系表中的数据，所以不需要去同步修改关系表中的数据
         */
        if (teamUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamUpdateRequest.getId();
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //必须是当前用户或者管理员才能修改队伍信息
        Team oldTeam = this.getById(id);
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }

        long oldTeamUserId = oldTeam.getUserId();
        long loginUserId = loginUser.getId();
        if (oldTeamUserId != loginUserId && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }

        //如果房间为私密，则必须设置密码
        TeamStatusEnum enumByValue = TeamStatusEnum.getEnumByValue(teamUpdateRequest.getStatus());
        if (enumByValue.equals(TeamStatusEnum.SECRET)) {
            if (StringUtils.isBlank(teamUpdateRequest.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密放假必须设置密码");
            }
        }
        Team team = new Team();
        BeanUtils.copyProperties(teamUpdateRequest, team);
        return this.updateById(team);
    }

    /**
     * 加入队伍
     *
     * @param teamJoinRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean joinTeam(@RequestBody TeamJoinRequest teamJoinRequest, User loginUser) {
        Long teamId = teamJoinRequest.getTeamId();
        //队伍要存在
        if (teamId <= 0 || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //队伍没过期
        Team team = this.getById(teamId);
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime) || expireTime == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //禁止加入私有队伍
        Integer teamStatus = team.getStatus();
        TeamStatusEnum enumByValue = TeamStatusEnum.getEnumByValue(teamStatus);
        if (TeamStatusEnum.PRIVATE.equals(enumByValue)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");
        }

        //判断密码对错
        String password = team.getPassword();
        String JoinPassword = teamJoinRequest.getPassword();
        if (TeamStatusEnum.SECRET.equals(JoinPassword)) {
            if (StringUtils.isBlank(JoinPassword) || !password.equals(JoinPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }

        Long loginUserId = loginUser.getId();
        // 只有一个线程能获取到锁
        RLock lock = redissonClient.getLock("yupao:join_team");
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    //用户最多加入五个队伍
                    QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", loginUserId);
                    long count = userTeamService.count(queryWrapper);
                    if (count > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多加入五个队伍");
                    }

                    //不能重复加入自己已的队伍
                    QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("teamId", teamId);
                    userTeamQueryWrapper.eq("userId", loginUserId);
                    long userTeamCount = userTeamService.count(userTeamQueryWrapper);
//                    log.info("userTeamCount：" + userTeamCount);
                    if (userTeamCount > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能加入自己已队伍");
                    }

                    //判断队伍是否已满
                    Integer maxNum = team.getMaxNum();
                    QueryWrapper<UserTeam> countQueryWrapper = new QueryWrapper<>();
                    countQueryWrapper.eq("teamId", teamId);
                    long maxNumCount = userTeamService.count(countQueryWrapper);
                    if (maxNumCount >= maxNum) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "当前队伍已满");
                    }

                    //已加入队伍人数加1
                    QueryWrapper<Team> teamQueryWrapper = new QueryWrapper<>();
                    teamQueryWrapper.eq("id", teamJoinRequest.getTeamId());
                    Team joinNumTeam = this.getOne(teamQueryWrapper);
                    //在加入队伍时，先从队伍关系表中查询自己是否已经加入队伍，如果已经加入队伍，则这里就不+1
                    QueryWrapper<UserTeam> hasJoinNumUserTeamQueryWrapper = new QueryWrapper<>();
                    hasJoinNumUserTeamQueryWrapper.eq("teamId", teamJoinRequest.getTeamId());
                    hasJoinNumUserTeamQueryWrapper.eq("userId", loginUserId);
                    long teamCount = userTeamService.count(hasJoinNumUserTeamQueryWrapper);
                    if (teamCount < 1) {
                        joinNumTeam.setHasJoinNum(joinNumTeam.getHasJoinNum() + 1);
                    }
                    boolean updateJoinNumTeam = this.updateById(joinNumTeam);
                    if (!updateJoinNumTeam) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "已加入人数更新失败");
                    }

                    //更新关系表
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(loginUserId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            log.error("Join Team lock Interrupted error", e);
            return false;
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("Join Team unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }


    }

    /**
     * 退出队伍
     *
     * @param teamQuitRequest
     * @param loginUser
     * @return
     */
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        //1. 校验请求参数
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //
        //2. 校验队伍是否存在
        long teamId = teamQuitRequest.getTeamId();
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //
        //3. 校验我是否已加入队伍
        long loginUserId = loginUser.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        queryWrapper.eq("userId", loginUserId);
        long count = userTeamService.count(queryWrapper);
        if (count == 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不在当前队伍中");
        }
        //
        //4. 如果队伍
        //   1. 只剩一人，队伍解散
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        long teamPersonNum = userTeamService.count(userTeamQueryWrapper);
        if (teamPersonNum == 1) {
            userTeamService.remove(userTeamQueryWrapper); //删除关系表信息
            return this.removeById(teamId); //删除队伍信息
        } else {
            //   2. 还有其他人
            //      1. 如果是队长退出队伍，权限转移给第二早加入的用户 —— 先来后到
            long teamUserId = team.getUserId();
            if (teamUserId == loginUserId) {
                // 把队伍转移给最早加入的用户
                // 1. 查询已加入队伍的所有用户和加入时间
                QueryWrapper<UserTeam> ListQueryWrapper = new QueryWrapper<>();
                ListQueryWrapper.eq("teamId", teamId);
                ListQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(ListQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam userTeam = userTeamList.get(1); //第二加入队伍的人
                Long userId = userTeam.getUserId(); //第二加入队伍的人的userId
                //更新当前队伍的队长信息
                QueryWrapper<UserTeam> getJoinTeamNumQueryWrapper = new QueryWrapper<>();
                getJoinTeamNumQueryWrapper.eq("teamId", teamId);
                int joinTeamNum = (int) userTeamService.count(getJoinTeamNumQueryWrapper);
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(userId); //将team表的队长设置为第二加入队伍的人
                //因为是最后才删除的关系表中的关系，所以这里要提前减一
                updateTeam.setHasJoinNum(joinTeamNum - 1);   //已加入人数减1
                boolean updateResult = this.updateById(updateTeam);
                if (!updateResult) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队长信息错误");
                }

            } else {
                //如果当前用户不是队长，退出的话已加入人数还是要减一
                QueryWrapper<UserTeam> getJoinTeamNumQueryWrapper = new QueryWrapper<>();
                getJoinTeamNumQueryWrapper.eq("teamId", teamId);
                //拿到关系表中当前加入队伍的有多少人
                int joinTeamNum = (int) userTeamService.count(getJoinTeamNumQueryWrapper);
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                //因为是最后才删除的关系表中的关系，所以这里要提前减一
                updateTeam.setHasJoinNum(joinTeamNum - 1);   //已加入人数减1
                boolean updateResult = this.updateById(updateTeam);
                if (!updateResult) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队长信息错误");
                }
            }
        }
        QueryWrapper<UserTeam> removeUserTeamQueryMapper = new QueryWrapper<>();
        removeUserTeamQueryMapper.eq("teamId", teamId);
        removeUserTeamQueryMapper.eq("userId", loginUserId);
        return userTeamService.remove(removeUserTeamQueryMapper); //删除关系表中的当前用户
    }


    /**
     * 解散队伍
     *
     * @param id        队伍id teamId
     * @param loginUser 当前登录用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeam(long id, User loginUser) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //校验队伍是否存在
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //校验你是不是队伍的队长
        Long loginUserId = loginUser.getId();
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUserId);
        queryWrapper.eq("id", id);
        long count = this.count(queryWrapper);
        if (count == 0) {
            throw new BusinessException(ErrorCode.NO_AUTH, "非队长无法解散队伍");
        }
        //移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", id);
        boolean result = userTeamService.remove(userTeamQueryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除关联信息失败");
        }
        //移除队伍信息
        return this.removeById(id);


    }

    /**
     * 返回当前更新队伍的队伍信息
     *
     * @param id 队伍id
     * @return
     */
    @Override
    public TeamGetQuery getUpdateTeamById(long id) {
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "当前队伍不存在");
        }
        TeamGetQuery teamGetQuery = new TeamGetQuery();
        BeanUtils.copyProperties(team, teamGetQuery);
        return teamGetQuery;
    }

    /**
     * 获取我创建的队伍
     *
     * @param loginUser
     * @return
     */
    @Override
    public List<TeamUserVO> getMyCreateTeam(User loginUser, String searchText) {
        Long loginUserId = loginUser.getId();
        if (loginUserId == null || loginUserId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUserId);
        //用户名和描述同时联合查询
        if (StringUtils.isNotBlank(searchText)) {
            queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
        }
        List<Team> teamList = this.list(queryWrapper);
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teamList) {
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(loginUser, userVO);
            teamUserVO.setCreateUser(userVO);
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }

    /**
     * 获取我加入的队伍
     *
     * @param loginUser
     * @param searchText
     * @return
     */
    @Override
    public List<TeamUserVO> getMyJoinTeam(User loginUser, String searchText) {
        Long loginUserId = loginUser.getId();
        if (loginUserId == null || loginUserId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUserId);
        //用户名和描述同时联合查询
        if (StringUtils.isNotBlank(searchText)) {
            queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
        }
        List<UserTeam> teamList = userTeamService.list(queryWrapper);
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (UserTeam userTeam : teamList) {
            Long teamId = userTeam.getTeamId();
            QueryWrapper<Team> teamQueryWrapper = new QueryWrapper<>();
            teamQueryWrapper.eq("id", teamId);
            Team team = this.getOne(teamQueryWrapper);
            Long userId = team.getUserId();
            User user = userService.getById(userId);
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            teamUserVO.setCreateUser(userVO);
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }


}




